export const DI = {
    ids: new Map(),
    TARGET: '$di$target',
    DEPENDENCIES: '$di$dependencies',
    getDep(ctor) {
        return ctor[DI.DEPENDENCIES] || [];
    },
};

function storeServiceDep(id: Function, target: Function, index: number) {
    const t: any = target;

    if (t[DI.TARGET] === target) {
        t[DI.DEPENDENCIES].push({ id, index });
    } else {
        t[DI.DEPENDENCIES] = [{ id, index }];
        t[DI.TARGET] = target;
    }
}

/**
 * Identifies a service of type `T`.
 */
export interface ServiceIdentifier<T> {
    (...args: any[]): void;
    type: T;
}

export interface ServicesAccessor {
    get<T>(id: ServiceIdentifier<T>): T;
}

export function createDiWrapper<T>(serviceId): ServiceIdentifier<T> {
    if (DI.ids.has(serviceId)) {
        return DI.ids.get(serviceId);
    }

    const id = <any>function (target: Function, key: string, index: number) {
        if (arguments.length !== 3) {
            throw new Error('DI: Decorator can only be used on a parameter');
        }
        storeServiceDep(id, target, index);
    };

    id.toString = () => `@inject(${serviceId})`;

    DI.ids.set(serviceId, id);
    return id;
}
